package com.quectel.business.contentaudit.local;

import java.util.*;

/**
 * @author zhangheng
 * @email nolan.zhang@quectel.com
 * @date 2023/5/26 16:46
 * @description new java file header...
 */
public class SensitiveWordsFilter {

    private SensitiveWordsFilter() {
        throw new IllegalStateException("Utility class");
    }


    public static Map<String, ACNode> sensitiveWord(Map<String, List<String>> sensitiveWordsMap) {
        HashMap<String, ACNode> trieMap = new HashMap<>(sensitiveWordsMap.size());

        for (Map.Entry<String, List<String>> entry : sensitiveWordsMap.entrySet()) {
            // 敏感词组名称或ID
            String groupName = entry.getKey();
            // 敏感词列表
            List<String> wordList = entry.getValue();
            ACNode root = new ACNode();
            for (String word : wordList) {
                insertWord(root,word);
            }
            // 构建Fail指针，以在状态转移时快速跳转
            buildFailPointers(root);

            // 将Trie树和敏感词组对应起来
            trieMap.put(groupName, root);
        }
        //缓存
        return trieMap;
    }

    public static void insertWord(ACNode root, String word) {
        ACNode cur = root;
        for (int i = 0; i < word.length(); i++) {
            char ch = word.charAt(i);
            if (!cur.children.containsKey(ch)) {
                cur.children.put(ch, new ACNode());
            }
            cur = cur.children.get(ch);
        }
        cur.isEndOfWord = true;
        cur.word = word;
    }

    public static void buildFailPointers(ACNode root) {
        Deque<ACNode> queue = new ArrayDeque<>();
        for (ACNode child : root.children.values()) {
            queue.offer(child);
            child.fail = root;
        }
        while (!queue.isEmpty()) {
            ACNode cur = queue.poll();
            for (Map.Entry<Character, ACNode> entry : cur.children.entrySet()) {
                char ch = entry.getKey();
                ACNode child = entry.getValue();
                ACNode failNode = cur.fail;
                while (failNode != null && !failNode.children.containsKey(ch)) {
                    failNode = failNode.fail;
                }
                if (failNode != null) {
                    child.fail = failNode.children.get(ch);
                    if (child.fail.isEndOfWord && child.word == null) {
                        // 如果child的fail指向一个结尾节点，且当前节点尚未匹配到任何敏感词，则将其匹配到fail节点的敏感词
                        child.word = child.fail.word;
                    }
                } else {
                    child.fail = root;
                }
                queue.offer(child);
            }
        }
    }

    /**
     * 检测是否存在敏感词，返回所有匹配的敏感词组
     *
     * @param trieMap
     * @param text
     * @return
     */
    public static String check(Map<String, ACNode> trieMap, String text) {
        if (trieMap == null) {
            return null;
        }

        for (Map.Entry<String, ACNode> entry : trieMap.entrySet()) {
            // 敏感词组名称或ID
            String groupName = entry.getKey();
            // 对应的Trie树
            ACNode cur = entry.getValue();
            // 过滤空格、标点等字符，只保留中英文和数字
            String filteredText = text.replaceAll("[^\u4e00-\u9fa5a-zA-Z0-9]", "");
            for (int i = 0; i < filteredText.length(); i++) {
                char ch = filteredText.charAt(i);
                while (cur != entry.getValue() && !cur.children.containsKey(ch)) {
                    // 当前状态没有匹配到字符ch，跳转到Fail指针指向的状态，尝试匹配下一个字符
                    cur = cur.fail;
                }
                if (cur.children.containsKey(ch)) {
                    cur = cur.children.get(ch);
                    if (cur.isEndOfWord) {
                        // 匹配到了一个敏感词的结尾节点，返回该敏感词组名称
                        return groupName;
                    }
                }
            }
        }
        return null;
    }

}
